﻿#include "imagehelper.h"
#include "plantumlgeneratertask.h"
#include "plantumlpreview.h"
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QMessageBox>
#include <QPainter>
#include <QResizeEvent>
#include <QSvgRenderer>
#include <QThreadPool>
#include <ui_plantumlpreview.h>
#include <QClipboard>

PlantUMLPreview::PlantUMLPreview(QWidget *parent, QsciScintilla *pEdit) : QMainWindow(parent)
  , ui(new Ui::PlantUMLPreview)
  , m_currentEdit(pEdit)
  , s_qrcUmljar("://assets/2023-02-13-10-36-53-plantuml-plantuml-1.2023.1.jar")
  , s_fileUmlJar("plugin/2023-02-13-10-36-53-plantuml-plantuml-1.2023.1.jar")
  , m_installedJavaSDK(false)
{
    ui->setupUi(this);
    setWindowTitle("快速预览UML图的插件");
    setAttribute(Qt::WA_DeleteOnClose);

    // 启动时检查文件是否存在，不存在则导出一份
    if (!QFileInfo(s_fileUmlJar).exists()) {
        checkAndCopyPlantUmlJarFile();
    }

    connect(m_currentEdit, &QsciScintilla::textChanged,this, &PlantUMLPreview::doRegeneraterPlantUmlImage);

    // 首次启动插件将执行生成与环境检查
    emit checkJavaInstalled();
    emit doRegeneraterPlantUmlImage();

    // 添加上下文菜单
    QAction *actionCopy = new QAction("复制当前图片");
    QAction *actionCopyOrigin = new QAction("复制原始图片");

    connect(actionCopy, &QAction::triggered, this, [=](bool checked){
        qDebug() << "action: trigger";
        if (!m_currenUmlPixmap.isNull()) {
            QClipboard *clip = QApplication::clipboard();
            clip->setPixmap(m_currenUmlPixmap);
        }
    });
    connect(actionCopyOrigin, &QAction::triggered, this, [=](bool checked){
        qDebug() << "action: trigger";
        if (!m_currentUmlData.isNull()) {
            QPixmap pixmap;
            pixmap.loadFromData(m_currentUmlData);
            QClipboard *clip = QApplication::clipboard();
            clip->setPixmap(pixmap);
        }
    });

    QMenu *menu = new QMenu();
    menu->addAction(actionCopy); // 复制当前图片
    menu->addAction(actionCopyOrigin); // 复制原始图片

    ui->label->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
    connect(ui->label, &QLabel::customContextMenuRequested, this, [=](const QPoint &pos){
        if (!m_currenUmlPixmap.isNull()) {
            menu->exec(QCursor::pos());
        }
    });
}

void PlantUMLPreview::refreshUmlImage()
{
    qDebug() << "refreshFinished: 即将更新 Uml 数据为预览图";

    if (!m_currentUmlData.isNull()) {
        QSize size = ui->label->size();
        QPixmap pixmap;

        // 直接使用数据加载为图片
        if (m_currentUmlGenType == "png") {
            pixmap.loadFromData(m_currentUmlData);
        // svg 将使用 ImageHelper::pixmapFromSvgString
        } else {
            pixmap = ImageHelper::pixmapFromSvgString(size, m_currentUmlData);
        }

        if (!pixmap.isNull()) {
            QPixmap convertPixmap;
            convertPixmap = ImageHelper::scaledKeepAspectRatio(size, pixmap);
            // 进行屏幕像素比例计算(对 Svg )
                // png 图像不进行调整
            if (m_currentUmlGenType == "svg") {
                const qreal ratio = qApp->devicePixelRatio();
                convertPixmap.setDevicePixelRatio(ratio);
            }
            ui->label->setPixmap(convertPixmap);
            m_currenUmlPixmap = convertPixmap;
        }
    } else {
        qDebug() << "loadFromData: Uml 数据为空, 不更新预览图";
    }

    // 确保处理图片后再次检查一次内容
    emit doRegeneraterPlantUmlImage();
}

/**
 * @brief PlantUMLPreview::regenerateImage 触发图片生成函数
 * 1. 该函数由切换 png/svg 选中、与编辑器中 textChanged 等事件触发
 * 2. 在触发生成图片时，进程可能会影响 UI，所以使用多线程任务形式处理
 */
void PlantUMLPreview::doRegeneraterPlantUmlImage()
{
    qDebug() << "GeneraterPlantUmlImage";

    QString content = m_currentEdit->text();
    QString outType = ui->png->isChecked() ? "png": "svg";

    if (!ui->png->isEnabled())
        return;

    if (m_currnetUmlContent == content)
        return;

    m_currentUmlGenType = outType;
    m_currnetUmlContent = content;
    QSize pixSize = ui->label->size();

    /**
     * @brief task 图片生成任务，实现于 QRunnable 接口 run 函数
     * 并交由线程池进行启动任务。
     *
     * 在放在线程池启动前，将为其设置工作参数，在由线程池调用 run 时使用。
     * 在放在线程池启动前，使用其函数进行接收任务完成的数据。
     *
     */
    PlantUMLGeneraterTask *task = new PlantUMLGeneraterTask;
    task->setGeneraterArguments(s_fileUmlJar, outType, content, pixSize);
    connect(task, &PlantUMLGeneraterTask::generated, this, [&](QByteArray data){
        setUiRadioButtonEnabled(true);
        m_currentUmlData = data;
        emit refreshUmlImage();
    });

    QThreadPool::globalInstance()->start(task);
    setUiRadioButtonEnabled(false);
}

void PlantUMLPreview::on_png_clicked()
{
    // 为了可以重新生成，而将此处进行改变
    m_currnetUmlContent.clear();
    emit doRegeneraterPlantUmlImage();
}

void PlantUMLPreview::on_svg_clicked()
{
    // 为了可以重新生成，而将此处进行改变
    m_currnetUmlContent.clear();
    emit doRegeneraterPlantUmlImage();
}

bool PlantUMLPreview::checkJavaInstalled()
{
    m_installedJavaSDK = true;

    QProcess process;
    // 这里的修改是：
        // 在 Linux 中 java --version 与 java -version 均可使用
        // 发现在 Windows 中只支持 java -version，并进行此处修复
    process.start("java", QStringList("-version"));

    if (!process.waitForStarted()
        || !process.waitForFinished()
        ||  process.exitCode() != 0) {
        m_installedJavaSDK = false;
    }

    // 标记窗口：将提示无法进行预览
    if (!m_installedJavaSDK) {
        // QMessageBox::warning(this, "提示", "您未安装 Java 运行环境，无法使用此插件", QMessageBox::Ok);
        setWindowTitle("您未安装 Java 运行环境，无法预览此插件");
    }
   
    return m_installedJavaSDK;
}

/**
 * @brief  从 qrc 资源中导出一份 plantuml.jar 
 * @note   
 * @retval None
 */
void PlantUMLPreview::checkAndCopyPlantUmlJarFile()
{
    QFile internal(s_qrcUmljar);
    qDebug() << "外部 plantuml.jar 未找到!";
    qDebug() << "创建目录" << QDir().mkpath("plugin");
    qDebug() << "检查内部文件是否存在" << internal.exists();

    // ://assets/2023-02-13-10-36-53-plantuml-plantuml-1.2023.1.jar
    // QFile plantumljar(s_fileUmlJar);
    // plantumljar.open(QIODevice::WriteOnly);
    // plantumljar.write()
    // plantumljar.close();

    // 使用 copy 方式输出文件
    QFile::copy(s_qrcUmljar, s_fileUmlJar);
}

void PlantUMLPreview::setUiRadioButtonEnabled(bool enable)
{
    ui->png->setEnabled(enable);
    ui->svg->setEnabled(enable);
}

/**
 * @brief  在调整窗口时，重新从 uml 数据中更新当前内容，而不是重新生成图
 * @note   
 * @param  *event: 
 * @retval None
 */
void PlantUMLPreview::resizeEvent(QResizeEvent *event)
{
    refreshUmlImage();
}
